#include "myFunctions.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sstream>

using namespace std;

/**
Lee de un archivo valores de varias entradas y una salida separados por comas
*/
void readFile(mdouble&X, vector<int>&Y, string file, bool insX0){
	ifstream archi;
	archi.open(file.c_str());
	if(archi.is_open()){
		while(!archi.eof()){
			string str_line;
			getline(archi,str_line);
			//char* line;
			//line = &str_line[0];
			if(!archi.eof())
				explode(str_line, X, Y, insX0);
			//explode(line, X, Y);
		}
	archi.close();
	}
}

/**
Dado un string, separa su contenido, separado por comas,
en valores numericos y los agrega a los vectores pasados por referencia
*/
void explode(string line, mdouble&X, vector<int>&Y, bool insX0){
	vector<double>Aux;
	if(insX0)
		Aux.push_back(-1); //valor que prop movilidad a la recta(abcisa)
	char Buffer[25]; //maximo 25 cifras
	//for(int l=0;l<25;l++) Buffer[l] = '\0';
	int i = 0;
	for (int k=0; line[k] != '\0'; k++){
		if (line[k] != ','){
			Buffer[i] = line[k];
			i++;
		}else{
			Aux.push_back(atof(Buffer));
			for(int l=0;l<25;l++) Buffer[l] = '\0';
			i = 0;
		}
	}
	Y.push_back(atoi(Buffer)); //el ultimo que lei es la salida	
	X.push_back(Aux);	   //agrego las nuevas entradas	
}

/**
Dado un char*, separa su contenido, separado por comas,
en valores numericos y los agrega a los vectores pasados por referencia
*/
void explode(char* line, mdouble&X, vector<int>&Y, bool insX0){
	vector<double>Aux;
	if(insX0)
		Aux.push_back(-1); //valor que prop movilidad a la recta(abcisa)
	char Buffer[25]; //maximo 25 cifras
	for(int l=0;l<25;l++) Buffer[l] = '\0';
	int i = 0;
	for (int k=0; line[k] != '\0'; k++){
		if (line[k] != ','){
			Buffer[i] = line[k];
			i++;
		}else{
			Aux.push_back(atof(Buffer));
			for(int l=0;l<25;l++) Buffer[l] = '\0';
			i = 0;
		}
	}
	Y.push_back(atoi(Buffer)); //el ultimo que lei es la salida	
	X.push_back(Aux);	   //agrego las nuevas entradas	
}


/**
Guarda en un archivo, datos separados por comas
*/
void saveFile(mdouble&X, vector<int>&y, string file){
	ofstream archi(file.c_str(), ios::trunc);
	unsigned int nRow = X.size();
	unsigned int nCol = X[0].size();
	if(archi.is_open()){	
		for (unsigned int i=0;i<nRow;i++){ //habria que mejorar como lee el tamaño, por ahora funca
			for (unsigned int j=0;j<nCol;j++){
				archi<<X[i][j]<<",";
			}
			archi<<y[i]<<endl;
		}
		archi.close();
	}
}


/**
Carga la configuracion inicial del archivo "config.txt"
*/
void loadConfig(double &delta, int &nMaxIt, double &tasa, bool &zero, bool &error, bool &maxIt, int &n, bool &varW){
	ifstream archi;
	archi.open("config.txt");
	if(archi.is_open()){
		string str_line;
		char* line;

		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		zero = atoi(line);

		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		error = atoi(line);

		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		maxIt = atoi(line);

		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		varW = atoi(line);		

		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		delta = atof(line);
		
		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		nMaxIt = atoi(line);
		
		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		tasa = atof(line);

		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		n = atoi(line);
				
		archi.close();
	}

}

/**
Dado un archivo con unos pocos valores de entradas y salida, genera un nuevo archivo
de "n" filas, con una desviacion "desv" en torno a los primeros
*/
void genRandom(string file, string new_file, double desv, int n){
	mdouble X,Xgen;
	vector<int> y, ygen;
	readFile(X,y,file,false);
	int nRow = X.size();
	int nCol = X[0].size(); 
	srand(time(NULL));
	int i;
	double insert;
	for(int j=0; j<n; j++){ 
		vector<double> Aux;
		i = int(rand() % nRow);
		for(int k=0;k<nCol;k++){
			double percentage = double(rand() % int(2*desv) - desv);
			insert = X[i][k] +  X[i][k]*percentage/100;
			Aux.push_back(insert);
		}
		Xgen.push_back(Aux);
		ygen.push_back(y[i]);
	}
	saveFile(Xgen, ygen, new_file);
}

/**
Con la matriz de entradas y el vector de salidas genera por ahora 20 particiones
de los datos, seleccionando porcTraining% de los datos para entrenamiento y el resto
para pruebas. Los guarda en archivos separados.
*/
void particionado(mdouble&X, vector<int>&Y, float porcTraining, int numpart){
	mdouble xAux,xAuxTest,X1;	//Los Aux son los vectores donde se van formando las nuevas particiones
	vector<int> yAux,yAuxTest,Y1;
	int tamanio = X.size();
	vector<vector<double> >::iterator it1;	//iterador para recorrer X1
	vector<int>::iterator it2;	//iteradores de los vectores y e yAux
	xAux.resize(int(X.size()*porcTraining)+1);
	yAux.resize(int(Y.size()*porcTraining)+1);
	
	for(int i=0; i<numpart; i++){	//Va a realizar numpart distintas particiones
		X1=X;	//Copio los datos de X en X1 para no modificar X y trabajar con X1 dentro de cada iteración
		Y1=Y;	//Lo mismo que para X
		int indice=0;
		
		while(indice <= (tamanio*porcTraining)){	
			srand(time(NULL));
			unsigned int azar = abs(rand() % X1.size());
			xAux[indice].resize(X[azar].size());

			copy(X1[azar].begin(), X1[azar].end(),xAux[indice].begin());
			yAux[indice] = Y1[azar];
			
			X1.erase(X1.begin()+azar);	//Borra la fila	para que en la proxima iteración no pueda ser elegida nuevamente
			Y1.erase(Y1.begin()+azar);
			indice++;
		}

		xAuxTest.resize(X1.size());
		yAuxTest.resize(Y1.size());

		//Copia los datos que quedaron sin elegir para ser usados para prueba.
		copy(X1.begin(), X1.end(),xAuxTest.begin());
		copy(Y1.begin(), Y1.end(),yAuxTest.begin());

		stringstream cvz;
		cvz<<i;
		string snumero=cvz.str();
		string nomArchi="Training/partTraining"+snumero+".txt";
		saveFile(xAux,yAux,nomArchi);	//Guarda el archivo con la partición de entrenamiento realizada
		nomArchi="Test/partTest"+snumero+".txt";
		saveFile(xAuxTest,yAuxTest,nomArchi);
		cout<<"Generando particionado..."<<endl;
	}
}

//Funcion signo
int sign(double x){
	return ((fabs(x)-x) ? -1 : 1); 
}

/**
Resuelve el producto punto x.w = y
*/
double prod_punto(vector<double> &x, vector<double> &w){
	int l = x.size();
	double y = 0;	
	for(int i=0;i<l;i++){
		y += x[i]*w[i];		
	}
	return y;
}

/**
Resuelve el producto matricial X w = y
*/
vector<double> prod_mat(mdouble &X, vector<double> &w){
	int l1 = X.size();
	vector<double> aux;
	aux.resize(l1); 
	for(int i=0;i<l1;i++){
		aux[i] = prod_punto(X[i],w);		
	}
	return aux;
}


